package utils

import (
	"crypto/md5"
	crypto_rand "crypto/rand"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"math"
	"math/big"
	"math/rand"
	"net/http"
	"net/url"
	"path"
	"reflect"
	"strconv"
	"time"
	"unicode"
)

// ParseStringFloat64 ...

// SixMathCode 6位数验证码
func SixMathCode() (code string) {
	code = fmt.Sprintf("%06v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000))
	return
}

func ToString(obj interface{}, precisionNum ...int) string {
	if InterfaceIsNil(obj) {
		return ""
	}
	switch reflect.TypeOf(obj).Kind() {
	default:
		return fmt.Sprintf("%v", obj)
	case reflect.Float32, reflect.Float64:
		if len(precisionNum) > 0 {
			format := fmt.Sprintf("%%.%df", precisionNum[0])
			return fmt.Sprintf(format, obj)
		}
		return fmt.Sprintf("%f", obj)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
		reflect.Int8, reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
		return fmt.Sprintf("%d", obj)
	}
}

func InterfaceIsNil(val interface{}) (flag bool) {
	if !reflect.ValueOf(val).IsValid() {
		return true
	}
	if IsReferenceType(reflect.ValueOf(val)) && reflect.ValueOf(val).IsNil() {
		return true
	}
	return
}

func IsReferenceType(value reflect.Value) bool {
	switch value.Kind() {
	case reflect.Ptr, reflect.Chan, reflect.Interface, reflect.Func, reflect.Map, reflect.Slice:
		return true
	default:
		return false
	}
}

func MustJson(v any) []byte {
	j, _ := json.Marshal(v)
	return j
}

// Md5Encode ...
func Md5Encode(str string) string {
	h := md5.New()
	h.Write([]byte(str))
	return hex.EncodeToString(h.Sum(nil))
}

func Sha1Encode(str string) string {
	h := sha1.New()
	h.Write([]byte(str))
	return hex.EncodeToString(h.Sum([]byte("")))
}

func ToInt(val interface{}) int {
	switch val.(type) {
	case float64:
		return int(val.(float64))
	case int32:
		return int(val.(int32))
	case int:
		return val.(int)
	case int8:
		return int(val.(int8))
	case int64:
		return int(val.(int64))
	case string:
		return ParseInt(val.(string), 0)
	}
	return 0
}

func ToIntArray(strs ...string) []int {
	convert := []int{}
	for _, s := range strs {
		convert = append(convert, ToInt(s))
	}
	return convert
}

func IfInterface(cond bool, a, b interface{}) interface{} {
	if cond {
		return a
	}
	return b
}

func GetCurrentTimeInMS() int64 {
	timestamp := time.Now().UnixNano() / 1e6
	return timestamp
}

func HasChineseChar(str string) bool {
	for _, ch := range []rune(str) {
		// 判断字符是否是中文
		if unicode.Is(unicode.Han, ch) {
			return true
		}
	}
	return false
}

func ByteToKMGStr(by int64) string {
	switch {
	case by > 0 && by < 1024*1024:
		return fmt.Sprintf("%.1fKB", float64(by)/(1024))
	case by > 1024*1024 && by < 1024*1024*1024:
		return fmt.Sprintf("%.1fMB", float64(by)/(1024*1024))
	case by > 1024*1024*1024:
		return fmt.Sprintf("%.1fGB", float64(by)/(1024*1024*1024))
	}
	return ""
}

// StructToMapWithTag 结构体转map，根据结构体tag(json)来设置key
func StructToMapWithTag(obj interface{}, noNil ...bool) map[string]interface{} {
	k := reflect.TypeOf(obj)
	v := reflect.ValueOf(obj)

	var data = make(map[string]interface{})
	for i := 0; i < k.NumField(); i++ {

		if len(noNil) > 0 && noNil[0] == true {
			if v.Field(i).IsZero() {
				continue
			}
		}
		data[k.Field(i).Tag.Get("json")] = v.Field(i).Interface()
	}
	return data
}

func GetRemoteFileSize(url string) (size int, err error) {
	response, err := http.Head(url)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer response.Body.Close()

	size, err = strconv.Atoi(response.Header.Get("Content-Length"))
	if err != nil {
		fmt.Println(err)
	}
	return
}

func GetRemoteFileName(rUrl string) (fileName string, err error) {
	u, err := url.Parse(rUrl)
	if err != nil {
		fmt.Println(err)
		return
	}
	fileName = path.Base(u.Path)
	return
}

func GetRemoteFileSHA(url string) (string, error) {
	resp, err := http.Get(url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	// 计算 SHA1 值
	hash := sha1.New()
	if _, err := io.Copy(hash, resp.Body); err != nil {
		return "", err
	}

	// 将 SHA1 值转换为字符串
	sha := hex.EncodeToString(hash.Sum(nil))
	return sha, nil
}

// Round 函数将浮点数四舍五入到指定的小数位数
func Round(num float64, decimalPlaces int) float64 {
	shift := math.Pow(10, float64(decimalPlaces))
	return math.Round(num*shift) / shift
}

// 三元运算
func TernaryOperator(condition bool, trueVal, falseVal interface{}) interface{} {
	if condition {
		return trueVal
	}
	return falseVal
}

// RangeRand 生成区间[-m, n]的安全随机数
func RangeRand(min, max int64) int64 {
	if min > max {
		panic("the min is greater than max!")
	}

	if min < 0 {
		f64Min := math.Abs(float64(min))
		i64Min := int64(f64Min)
		result, _ := crypto_rand.Int(crypto_rand.Reader, big.NewInt(max+1+i64Min))

		return result.Int64() - i64Min
	}

	result, _ := crypto_rand.Int(crypto_rand.Reader, big.NewInt(max-min+1))
	return min + result.Int64()
}

// Convent10To26 十进制转换成26进制
func Convent10To26(n int) string {
	var (
		Str  string = ""
		k    int
		temp []int //保存转化后每一位数据的值，然后通过索引的方式匹配A-Z
	)
	//用来匹配的字符A-Z
	Slice := []string{"", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}

	if n > 26 { //数据大于26需要进行拆分
		for {
			k = n % 26 //从个位开始拆分，如果求余为0，说明末尾为26，也就是Z，如果是转化为26进制数，则末尾是可以为0的，这里必须为A-Z中的一个
			if k == 0 {
				temp = append(temp, 26)
				k = 26
			} else {
				temp = append(temp, k)
			}
			n = (n - k) / 26 //减去Num最后一位数的值，因为已经记录在temp中
			if n <= 26 {     //小于等于26直接进行匹配，不需要进行数据拆分
				temp = append(temp, n)
				break
			}
		}
	} else {
		return Slice[n]
	}

	for _, value := range temp {
		Str = Slice[value] + Str //因为数据切分后存储顺序是反的，所以Str要放在后面
	}
	return Str
}

// RoundUpToInteger 向上取整
func RoundUpToInteger(total, size int) int {
	pages := total / size
	if total%size != 0 {
		pages++
	}
	return pages
}

func Float2(f float64) float64 {
	return math.Round(f*100) / 100
}
